/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Facade;

import exceptions.ExchangerServiceException;
import entity.Account;
import entity.BankTransaction;
import entity.CurrencyEE;
import entity.Customer;
import entity.Groups;
import exceptions.AccessDeniedException;
import exceptions.NotEnoughMoneyException;
import exceptions.AccountException;
import exceptions.TransactionLimitException;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Time;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJBException;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import ws.ExchangerServiceService;

/**
 *
 * @author Vojtisek
 */
@Stateless
@RolesAllowed("customer")
public class BankTransactionFacade implements BankTransactionFacadeLocal {

//    @WebServiceRef(wsdlLocation = "META-INF/wsdl/client/ExchangerServiceService/localhost_8080/WebServiceTJV-war/ExchangerServiceService.wsdl")
    ExchangerServiceService service;
    @PersistenceContext
    private EntityManager em;
    private static final Logger logger = Logger.getLogger("BankTransactionFacade");
    @Resource
    private SessionContext ctx;

    public BigDecimal getTotalBalance(CurrencyEE currency) throws Exception {
        Customer customer = getCurrentCustomer();
        BigDecimal totalBalance = BigDecimal.valueOf(0);
        for (Account account : customer.getAccounts()) {
            if (account.getCurrency() != currency) {
                try {
                    service = new ExchangerServiceService();
                    ws.ExchangerService port = service.getExchangerServicePort();
                    BigDecimal result = port.exchange(account.getBalance(), account.getCurrency().toString(), currency.toString());
                    totalBalance = totalBalance.add(result);
                } catch (Exception ex) {
                    logger.log(Level.SEVERE, ex.getMessage(), ex);
                    return null;
                }
            } else {
                totalBalance = totalBalance.add(account.getBalance());
            }
        }
        return totalBalance;
    }

    public Account getAccount(Integer id) throws Exception {
        return em.find(Account.class, id);
    }

    public void transfer(BankTransaction bankTransaction) throws AccessDeniedException, OptimisticLockException, ExchangerServiceException, EJBException, NotEnoughMoneyException, Exception {

        BigDecimal amountFrom = null;
        BigDecimal amountTo = null;
        BigDecimal balance = null;

        BigDecimal amount = bankTransaction.getAmount();
        CurrencyEE currency = bankTransaction.getCurrency();

        Account accountFrom = em.find(Account.class, bankTransaction.getFrom().getId());
        Account accountTo = em.find(Account.class, bankTransaction.getTo().getId());

        Customer customer = getCurrentCustomer();
        if (!customer.equals(accountFrom.getOwner())) {
            throw new AccessDeniedException();
        }

        CurrencyEE currencyFrom = accountFrom.getCurrency();
        CurrencyEE currencyTo = accountTo.getCurrency();
        BigDecimal limit = customer.getTransactionLimit();

        try {
            service = new ExchangerServiceService();
            ws.ExchangerService port = service.getExchangerServicePort();

            BigDecimal amountCZK = port.exchange(amount, currency.getName(), customer.getCurrency().name());
            if (amountCZK.compareTo(limit) == 1) {
                throw new TransactionLimitException(port.exchange(limit, customer.getCurrency().name(), currency.getName()));
            }

            if (currency.equals(currencyFrom)) {
                amountFrom = amount;
                if (currencyFrom.equals(currencyTo)) {
                    amountTo = amountFrom;
                } else {
                    amountTo = port.exchange(amountFrom, currencyFrom.getName(), currencyTo.getName());
                }
            } else {
                amountFrom = port.exchange(amount, currency.getName(), currencyFrom.getName());
                if (currencyFrom.equals(currencyTo)) {
                    amountTo = amountFrom;
                } else {
                    amountTo = port.exchange(amountFrom, currencyFrom.getName(), currencyTo.getName());
                }
            }

            if (accountFrom.getBalance().compareTo(amountFrom) == -1) {
                if (accountFrom.getCurrency() != currency) {
                    balance = port.exchange(accountFrom.getBalance(), currencyFrom.getName(), currency.getName());
                } else {
                    balance = accountFrom.getBalance();
                }
                throw new NotEnoughMoneyException(balance);
            }
        } catch (TransactionLimitException e) {
            throw e;
        } catch (NotEnoughMoneyException e) {
            throw e;
        } catch (Exception ex) {
            throw new ExchangerServiceException(ex);
        }

        accountFrom.setBalance(accountFrom.getBalance().subtract(amountFrom));
        accountTo.setBalance(accountTo.getBalance().add(amountTo));
        bankTransaction.setAmountFrom(amountFrom);
        bankTransaction.setAmountTo(amountTo);
        long time = new java.util.Date().getTime();
        bankTransaction.setTransferDate(
                new Date(time));
        bankTransaction.setTransferTime(
                new Time(time));
        em.persist(bankTransaction);
        try {
            em.flush();
        } catch (OptimisticLockException e) {
            throw new AccountException(e);
        }

    }

    public List<BankTransaction> findAccountTransactions(Object accountId) throws AccessDeniedException, Exception {
        Customer c = getCurrentCustomer();
        Account ac = em.find(Account.class, accountId);

        if (!c.getAccounts().contains(ac)) {
            throw new AccessDeniedException();
        }
        return em.createQuery("SELECT o FROM BankTransaction AS o WHERE o.from.id = :accountId OR o.to.id = :accountId").setParameter("accountId", accountId).getResultList();
    }

//    public List<BankTransaction> findReceived(Integer accountId) throws Exception {
//        return em.createQuery("select o from BankTransaction as o where o.to.id = :accountId").setParameter("accountId", accountId).getResultList();
//    }
//
//    public List<BankTransaction> findSended(Integer accountId) throws Exception {
//        return em.createQuery("select o from BankTransaction as o where o.from.id = :accountId").setParameter("accountId", accountId).getResultList();
//    }
    private Customer getCurrentCustomer() {
        String login = ctx.getCallerPrincipal().getName();
        Query query = em.createQuery("select c from Customer c where c.login=:login");
        query.setParameter("login", login);
        Customer cust = (Customer) query.getSingleResult();
        return cust;
    }
}
